package main.java.LeetCode.ClassicalAlgorithm.Timer;

import java.util.List;
import java.util.Scanner;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class SimpleTimer {

    private final TimingWheel<TimeNode> timingWheel = new TimingWheel<>();

    private final AtomicInteger status = new AtomicInteger(0);

    private Thread thread;

    private volatile long start;

    private ThreadPoolExecutor executor;


    private void init(){
        if(status.compareAndSet(1,2)){
            start = System.currentTimeMillis();
            while (!thread.isInterrupted()){
                int now = (int) (((System.currentTimeMillis() - start)/1000)%60);
                List<TimeNode> nodes = timingWheel.getAll(now);
                for (TimeNode node:nodes) {
                    executor.execute(node.runnable);
                    if(!node.delay){
                        int index = ((node.time + now) % 60);
                        timingWheel.set(index,node);
                    }
                }
                nodes.clear();
            }
        }
    }

    public void start(){
        if(status.compareAndSet(0,1)){
            executor = new ThreadPoolExecutor(1,2,10, TimeUnit.SECONDS,new LinkedBlockingQueue<>());
            thread = new Thread(this::init);
            thread.start();
        }
    }

    public void shutdown(){
        if(status.compareAndSet(2,0)){
            executor.shutdown();
            thread.interrupt();
            start = 0;
            executor = null;
            thread = null;
        }
    }

    private void delay(int delay,boolean isDelay,Runnable runnable){
        if(status.get() == 2) {
            int now = (int)((System.currentTimeMillis() - start)/1000%60) + delay;
            timingWheel.set(now%60,new TimeNode(delay,isDelay,runnable));
        }else
            timingWheel.set(delay%60, new TimeNode(delay,isDelay,runnable));
    }

    public void schedule(int time,Runnable runnable){
        delay(time,false,runnable);
    }

    public void delay(int delay,Runnable runnable){
        delay(delay,true,runnable);
    }

    private static class TimeNode{
        Runnable runnable;
        boolean delay;
        int time;

        public TimeNode(int time,boolean delay,Runnable runnable){
            this.time = time;
            this.delay = delay;
            this.runnable = runnable;
        }

        public TimeNode(Runnable r){
            this(0,true,r);
        }
    }

    public static void main(String[] args) {
        SimpleTimer timer = new SimpleTimer();

        timer.schedule(1, () -> System.out.println("A task doing"));
        timer.schedule(1, () -> System.out.println("B task doing"));

        timer.start();

        new Scanner(System.in).next();

        timer.shutdown();
    }

}
